package com.yuelao.yuelao_backend.service.impl;

import com.yuelao.yuelao_backend.config.MinioConfig;
import com.yuelao.yuelao_backend.service.FileUploadService;
import com.yuelao.yuelao_backend.util.UserContextUtil;
import io.minio.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * MinIO文件上传服务实现
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class FileUploadServiceImpl implements FileUploadService {

    private final MinioClient minioClient;
    private final MinioConfig minioConfig;

    // 允许的图片文件类型
    private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList(
            "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp"
    );

    // 允许的语音文件类型
    private static final List<String> ALLOWED_VOICE_TYPES = Arrays.asList(
            "audio/mpeg", "audio/mp3", "audio/wav", "audio/amr", "audio/ogg"
    );

    // 允许的视频文件类型
    private static final List<String> ALLOWED_VIDEO_TYPES = Arrays.asList(
            "video/mp4", "video/avi", "video/mov", "video/wmv", "video/flv"
    );

    // 文件大小限制（字节）
    private static final long MAX_IMAGE_SIZE = 10 * 1024 * 1024; // 10MB
    private static final long MAX_VOICE_SIZE = 50 * 1024 * 1024; // 50MB
    private static final long MAX_VIDEO_SIZE = 100 * 1024 * 1024; // 100MB

    @Override
    public String uploadFile(MultipartFile file, String fileType) {
        // 获取当前用户ID
        Long userId = UserContextUtil.getCurrentUserId();
        if (userId == null) {
            throw new RuntimeException("用户未登录");
        }
        return uploadFile(file, fileType, userId);
    }

    @Override
    public String uploadFile(MultipartFile file, String fileType, Long userId) {
        try {
            // 验证文件类型和大小
            if (!isAllowedFileType(file, fileType)) {
                throw new RuntimeException("不支持的文件类型或文件大小超出限制");
            }

            if (userId == null) {
                throw new RuntimeException("用户ID不能为空");
            }

            // 使用用户ID作为bucket名称
            String userBucketName = "user-" + userId;
            
            // 确保用户bucket存在
            ensureBucketExists(userBucketName);

            // 生成文件名（按日期分类）
            String fileName = generateFileName(file, fileType);

            // 上传文件
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(userBucketName)
                            .object(fileName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );

            // 返回文件访问URL
            String fileUrl = minioConfig.getFileUrlPrefix() + "/" + userBucketName + "/" + fileName;
            log.info("文件上传成功，访问URL: {}", fileUrl);
            return fileUrl;

        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }

    @Override
    public boolean deleteFile(String fileUrl) {
        try {
            // 从URL中提取bucket名和文件名
            String[] bucketAndFileName = extractBucketAndFileNameFromUrl(fileUrl);
            if (bucketAndFileName == null) {
                return false;
            }

            String bucketName = bucketAndFileName[0];
            String fileName = bucketAndFileName[1];

            // 删除文件
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build()
            );

            return true;
        } catch (Exception e) {
            log.error("文件删除失败: {}", e.getMessage(), e);
            return false;
        }
    }

    @Override
    public boolean isAllowedFileType(MultipartFile file, String fileType) {
        String contentType = file.getContentType();
        long fileSize = file.getSize();

        switch (fileType.toLowerCase()) {
            case "image":
                return ALLOWED_IMAGE_TYPES.contains(contentType) && fileSize <= MAX_IMAGE_SIZE;
            case "voice":
                return ALLOWED_VOICE_TYPES.contains(contentType) && fileSize <= MAX_VOICE_SIZE;
            case "video":
                return ALLOWED_VIDEO_TYPES.contains(contentType) && fileSize <= MAX_VIDEO_SIZE;
            default:
                return false;
        }
    }

    /**
     * 确保存储桶存在并设置访问策略
     */
    private void ensureBucketExists(String bucketName) throws Exception {
        boolean bucketExists = minioClient.bucketExists(
                BucketExistsArgs.builder()
                        .bucket(bucketName)
                        .build()
        );

        if (!bucketExists) {
            // 创建bucket
            minioClient.makeBucket(
                    MakeBucketArgs.builder()
                            .bucket(bucketName)
                            .build()
            );
            log.info("创建存储桶: {}", bucketName);
            
            // 设置bucket为公开读取权限
            setBucketPublicReadPolicy(bucketName);
        } else {
            // 检查并确保bucket有公开读取权限
            ensureBucketPublicReadPolicy(bucketName);
        }
    }

    /**
     * 设置bucket为公开读取权限
     */
    private void setBucketPublicReadPolicy(String bucketName) throws Exception {
        String policy = """
        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {"AWS": "*"},
                    "Action": ["s3:GetObject"],
                    "Resource": ["arn:aws:s3:::%s/*"]
                }
            ]
        }
        """.formatted(bucketName);

        minioClient.setBucketPolicy(
                SetBucketPolicyArgs.builder()
                        .bucket(bucketName)
                        .config(policy)
                        .build()
        );
        
        log.info("设置存储桶{}为公开读取权限", bucketName);
    }

    /**
     * 确保bucket有公开读取权限
     */
    private void ensureBucketPublicReadPolicy(String bucketName) throws Exception {
        try {
            String policy = minioClient.getBucketPolicy(
                    GetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .build()
            );
            
            // 如果没有策略或策略不包含公开读取，则设置
            if (policy == null || !policy.contains("\"Principal\":{\"AWS\":\"*\"}")) {
                setBucketPublicReadPolicy(bucketName);
            }
        } catch (Exception e) {
            // 如果获取策略失败，可能是没有策略，尝试设置
            setBucketPublicReadPolicy(bucketName);
        }
    }

    /**
     * 生成文件名
     */
    private String generateFileName(MultipartFile file, String fileType) {
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String originalFilename = file.getOriginalFilename();
        String extension = originalFilename != null && originalFilename.contains(".")
                ? originalFilename.substring(originalFilename.lastIndexOf("."))
                : getFileExtensionByType(fileType);
        
        return fileType + "/" + date + "/" + uuid + extension;
    }

    /**
     * 根据文件类型获取默认扩展名
     */
    private String getFileExtensionByType(String fileType) {
        switch (fileType.toLowerCase()) {
            case "image":
                return ".jpg";
            case "voice":
                return ".mp3";
            case "video":
                return ".mp4";
            default:
                return "";
        }
    }

    /**
     * 从URL中提取bucket名和文件名
     */
    private String[] extractBucketAndFileNameFromUrl(String fileUrl) {
        if (fileUrl == null || !fileUrl.contains(minioConfig.getFileUrlPrefix())) {
            return null;
        }
        
        String pathPart = fileUrl.substring(minioConfig.getFileUrlPrefix().length() + 1);
        String[] parts = pathPart.split("/", 2);
        
        if (parts.length < 2) {
            return null;
        }
        
        return new String[]{parts[0], parts[1]};
    }

    /**
     * 获取文件预览URL（带签名，有时效性）
     */
    public String getPresignedUrl(String fileUrl, int expiryHours) {
        try {
            String[] bucketAndFileName = extractBucketAndFileNameFromUrl(fileUrl);
            if (bucketAndFileName == null) {
                return fileUrl;
            }

            String bucketName = bucketAndFileName[0];
            String fileName = bucketAndFileName[1];

            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(fileName)
                            .expiry(expiryHours, TimeUnit.HOURS)
                            .build()
            );
        } catch (Exception e) {
            log.error("获取预览URL失败: {}", e.getMessage(), e);
            return fileUrl;
        }
    }

    /**
     * 验证文件是否存在并可访问
     */
    public boolean isFileAccessible(String fileUrl) {
        try {
            String[] bucketAndFileName = extractBucketAndFileNameFromUrl(fileUrl);
            if (bucketAndFileName == null) {
                return false;
            }

            String bucketName = bucketAndFileName[0];
            String fileName = bucketAndFileName[1];

            // 检查文件是否存在
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build()
            );
            
            return true;
        } catch (Exception e) {
            log.error("文件访问验证失败: {}", e.getMessage(), e);
            return false;
        }
    }
}